(function(window, undefined) {

  'use strict'

  // Helper functions.
  var getContext = function(width, height) {
    var canvas = document.createElement('canvas')
    canvas.setAttribute('width', width)
    canvas.setAttribute('height', height)
    return canvas.getContext('2d')
  };

  var getImageData = function(img, loaded) {
    var imgObj = new Image()
    var imgSrc = img.src || img
    if (imgSrc.substring(0, 5) !== 'data:') imgObj.crossOrigin = 'Anonymous'

    imgObj.onload = function() {
      var context = getContext(imgObj.width, imgObj.height)
      context.drawImage(imgObj, 0, 0)

      var imageData = context.getImageData(0, 0, imgObj.width, imgObj.height);
      loaded && loaded(imageData.data)
    }

    imgObj.src = imgSrc

  }

  var makeRGB = function(name) {
    return ['rgb(', name, ')'].join('')
  }

  var mapPalette = function(palette) {
    var arr = []
    for (var prop in palette) {
      arr.push(frmtPobj(prop, palette[prop]))
    };
    arr.sort(function(a, b) {
      return (b.count - a.count)
    })
    return arr
  }

  var fitPalette = function(arr, fitSize) {
    if (arr.length > fitSize) {
      return arr.slice(0, fitSize)
    } else {
      for (var i = arr.length - 1; i < fitSize - 1; i++) {
        arr.push(frmtPobj('0,0,0', 0))
      }
      return arr
    }
  }

  var frmtPobj = function(a, b) {
    return {
      name: makeRGB(a),
      count: b
    }
  }

  // RGBaster Object
  // ---------------
  //
  var PALETTESIZE = 10

  var RGBaster = {}

  RGBaster.colors = function(img, opts) {

    opts = opts || {}
    var exclude = opts.exclude || [], // for example, to exclude white and black:  [ '0,0,0', '255,255,255' ]
      paletteSize = opts.paletteSize || PALETTESIZE

    getImageData(img, function(data) {

      var colorCounts = {},
        rgbString = '',
        rgb = [],
        colors = {
          dominant: {
            name: '',
            count: 0
          },
          palette: []
        }

      var i = 0
      for (; i < data.length; i += 4) {
        rgb[0] = data[i]
        rgb[1] = data[i + 1]
        rgb[2] = data[i + 2]
        rgbString = rgb.join(',')

        // skip undefined data and transparent pixels
        if (rgb.indexOf(undefined) !== -1 || data[i + 3] === 0) {
          continue
        }

        // Ignore those colors in the exclude list.
        if (exclude.indexOf(makeRGB(rgbString)) === -1) {
          if (rgbString in colorCounts) {
            colorCounts[rgbString] = colorCounts[rgbString] + 1
          } else {
            colorCounts[rgbString] = 1
          }
        }

      }

      if (opts.success) {
        var palette = fitPalette(mapPalette(colorCounts), paletteSize + 1);
        opts.success({
          dominant: palette[0].name,
          secondary: palette[1].name,
          palette: palette.map(function(c) {
            return c.name
          }).slice(1)
        })
      }
    })
  }

  window.RGBaster = window.RGBaster || RGBaster;

})(window)
